/*
 * Author: lujjjh
 * Date: 2011/8/30
 */

(function () {
	var STATE_DEFAULT = 0;
	var STATE_STRING = 1;
	var STATE_CHARACTER = 2;
	var STATE_KEYWORD = 3;
	var STATE_COMMENTBLOCK = 4;
	var STATE_COMMENTLINE = 5;
	
	var keywordList = ['begin', 'end', 'false', 'true', 'if', 'else', 'elseif', 'class', 'function', 'return', 'while', 'do', 'namespace', 'select', 'case', 'catch', 'try', 'for', 'in', 'this', 'global', 'self', 'owner', 'var', 'def', 'null', 'and', 'not', 'or', 'break', 'continue', 'import', 'with', 'ctor', 'try', 'catch', 'eval', 'import','type', 'assert', 'assertf', 'error', 'rget', 'loadcode', 'dumpcode', 'collectgarbage', 'call', 'tostring', 'topointer', 'tonumber', 'sleep', 'execute', 'setlocale', 'setprivilege', 'null'];
	
	function attachEvent(obj, event, handler) {
		if (obj.attachEvent) {
			return obj.attachEvent('on' + event, handler);
		} else if (obj.addEventListener) {
			return obj.addEventListener(event, handler, false);
		}
		return false;
	}
	
	function listElements(parent, nodeName, cls) {
		var objList = parent.getElementsByTagName(nodeName);
		var result = [];
		for (var k in objList) {
			if (new RegExp('\\b' + cls + '\\b', 'i').test(objList[k].className)) {
				result.push(objList[k]);
			}
		}
		return result;
	}
	
	function styleContent(str, length, initState) {
		this.str = str;
		this.result = [];
		this.currentPos = -1;
		this.length = length;
		this.coloredLength = 0;
		this.ch = 0;
		this.state = initState || STATE_DEFAULT;
		this.read();
	}
	
	styleContent.prototype = {
		more: function () {
			return this.currentPos < this.length;
		},
		
		read: function () {
			if (this.more()) {
				this.currentPos++;
				this.lastCh = this.ch;
				this.ch = this.str.substr(this.currentPos, 1);
			} else {
				return false;
			}
		},
		
		readBack: function () {
			if (this.currentPos > 0) {
				this.currentPos -= 2;
				this.read();
			} else {
				return false;
			}
		},
		
		colorTo: function (posEnd, state) {
			this.state = state;
			var text = this.str.substring(this.coloredLength, posEnd + 1);
			text = text.split('\t').join('&nbsp;&nbsp;&nbsp;&nbsp;').split(' ').join('&nbsp;');
			text = text.split('<').join('&lt;').split('>').join('&gt;');
			text = text.split('\r\n').join('<br />').split('\n').join('<br />').split('\r').join('<br />');
			this.result.push({
				text: text,
				state: state
			});
			this.coloredLength = posEnd + 1;
		},
		
		writeResult: function (obj) {
			var html = '';
			for (var k in this.result) {
				var className;
				switch (this.result[k].state) {
					case STATE_STRING:
						className = 'aau_string';
						break;
					case STATE_CHARACTER:
						className = 'aau_character';
						break;
					case STATE_KEYWORD:
						className = 'aau_keyword';
						break;
					case STATE_COMMENTBLOCK:
						className = 'aau_commentblock';
						break;
					case STATE_COMMENTLINE:
						className = 'aau_commentline';
						break;
					default:
						className = 'aau_default';
				}
				var span = document.createElement('span');
				span.className = className;
				span.innerHTML = this.result[k].text;
				obj.appendChild(span);
			}
			return html;
		}
	}
	
	function inArray(arr, ele) {
		for (var k in arr) {
			if (arr[k] == ele) {
				return k;
			}
		}
		return -1;
	}
	
	function highlightCode(code, obj) {
		var sc = new styleContent(code, code.length);
		for (; sc.more(); sc.read()) {
			switch (sc.state) {
				case STATE_DEFAULT:
					if (sc.ch == '"') {
						sc.colorTo(sc.currentPos - 1, STATE_DEFAULT);
						sc.state = STATE_STRING;
					} else if (sc.ch == "'") {
						sc.colorTo(sc.currentPos - 1, STATE_DEFAULT);
						sc.state = STATE_CHARACTER;
					} else if (/[a-zA-Z_]/im.test(sc.ch) && ( ! sc.lastCh || /[\s;\(\)\{\},]/im.test(sc.lastCh))) {
						sc.colorTo(sc.currentPos - 1, STATE_DEFAULT);
						var word = '';
						while (/[a-zA-Z0-9_]/im.test(sc.ch)) {
							word += sc.ch;
							if (sc.more()) {
								sc.read();
							} else {
								break;
							}
						}
						if (/[\s;\(\)\{\},]/im.test(sc.ch) && inArray(keywordList, word) != -1) {
							sc.colorTo(sc.currentPos - 1, STATE_KEYWORD);
							sc.state = STATE_DEFAULT;
						}
						sc.readBack();
					} else if (sc.ch == '*' && sc.lastCh == '/') {
						sc.colorTo(sc.currentPos - 2, STATE_DEFAULT);
						sc.state = STATE_COMMENTBLOCK;
						sc.asteriskCount = 0;
						while (sc.ch == '*' && sc.more()) {
							sc.asteriskCount++;
							sc.read();
						}
						sc.readBack();
						sc.asteriskInputed = 0;
					} else if (sc.ch == '/' && sc.lastCh == '/') {
						sc.colorTo(sc.currentPos - 2, STATE_DEFAULT);
						sc.state = STATE_COMMENTLINE;
					}
					break;
				case STATE_STRING:
					if (sc.ch == '"') {
						sc.colorTo(sc.currentPos, STATE_STRING);
						sc.state = STATE_DEFAULT;
					}
					break;
				case STATE_CHARACTER:
					if (sc.ch == "'" && sc.lastCh != '\\') {
						sc.colorTo(sc.currentPos, STATE_CHARACTER);
						sc.state = STATE_DEFAULT;
					}
					break;
				case STATE_COMMENTBLOCK:
					if (sc.ch == '*') {
						sc.asteriskInputed++;
					} else if (sc.ch == '/') {
						if (sc.asteriskInputed == sc.asteriskCount) {
							sc.colorTo(sc.currentPos, STATE_COMMENTBLOCK);
							sc.state = STATE_DEFAULT;
						}
					} else {
						sc.asteriskInputed = 0;
					}
					break;
				case STATE_COMMENTLINE:
					if (sc.ch == '\n') {
						sc.colorTo(sc.currentPos, STATE_COMMENTLINE);
						sc.state = STATE_DEFAULT;
					}
					break;
			}
		}
		sc.colorTo(sc.length - 1, sc.state);
		
		return sc.writeResult(obj);
	}
	
	attachEvent(window, 'load', function () {
		var objList = listElements(document, 'textarea', 'aau');
		for (var k in objList) {
			var obj = objList[k];
			var div = document.createElement('div');
			div.className = obj.className;
			var divInner = document.createElement('div');
			divInner.className = obj.className + '_inner';
			highlightCode(obj.innerHTML, divInner);
			div.appendChild(divInner);
			obj.parentNode.insertBefore(div, obj);
			obj.style.display = "none";
		}
	});
})();
